package uscdb.service.impl;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.highlight.Fragmenter;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.SimpleFragmenter;
import org.apache.lucene.search.highlight.SimpleHTMLFormatter;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.search.FullTextQuery;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import uscdb.dao.LogicDaoI;
import uscdb.dao.LogicfileDaoI;
import uscdb.dao.VersionDaoI;
import uscdb.model.Tlogic;
import uscdb.model.Tlogicfile;
import uscdb.model.Tproduct;
import uscdb.model.Tversion;
import uscdb.pageModel.DataGrid;
import uscdb.pageModel.Logic;
import uscdb.pageModel.PageHelper;
import uscdb.service.LogicServiceI;
import uscdb.util.RarUtil;
import uscdb.util.ZipUtil;
import uscdb.util.StringUtil.ClobUtil;
import uscdb.util.StringUtil.StringUtil;

@Service
public class LogicServiceImpl implements LogicServiceI {
	/**
	 * Logger for this class
	 */
	private static final Logger logger = Logger.getLogger(LogicServiceImpl.class);

	@Autowired
	private LogicDaoI logicDao;

	@Autowired
	private LogicfileDaoI tlogicfileDao;

	@Autowired
	private SessionFactory sessionFactory;

	@Autowired
	private VersionDaoI versionDao;

	@Override
	public Map<String, Long> logicHeatChart(String number) {
		// 给number一个初始值
		int pageNum = 30;
		if (!StringUtil.isEmpty(number)) {
			pageNum = Integer.valueOf(pageNum);
		}
		Map<String, Long> logicHeatMap = new HashMap<>();
		Map<String, Object> params = new HashMap<>();
		String hql = "from Tlogic t ";
		// 获取前50条记录 降序
		PageHelper ph = new PageHelper(1, pageNum, "heat", "desc");
		List<Tlogic> tlogicList = logicDao.find(hql + orderHql(ph), params, ph.getPage(), ph.getRows());
		// 获取的记录封装在map
		for (Tlogic t : tlogicList) {
			logicHeatMap.put(t.getTitle(), t.getHeat());
		}
		return logicHeatMap;
	}

	/**
	 * 功能：增加logic点击率
	 * 
	 * @param logic
	 *            需要增加点击率的logic
	 */
	public void increaseHeat(Logic logic) {
		Tlogic tlogic = logicDao.get(Tlogic.class, logic.getId());
		if (null != tlogic) {
			logic.setHeat(logic.getHeat() + 1);
			tlogic.setHeat(tlogic.getHeat() + 1);
		}
	}

	@Override
	public DataGrid dataGrid(Logic logic, PageHelper ph) {
		DataGrid dg = new DataGrid();
		List<Logic> logicList = new ArrayList<Logic>();
		List<Tlogic> tlogicList = new ArrayList<Tlogic>();
		// 记录错误的tlogic，打印日志使用
		Tlogic tlogicErr = new Tlogic();
		try {
			// 默认查询出全部数据
			if (StringUtil.isEmpty(StringUtil.filterSpecialSymbol(logic.getTitle()))) {
				Map<String, Object> params = new HashMap<>();
				String hql = "from Tlogic t";
				tlogicList = logicDao.find(hql + orderHql(ph), params, ph.getPage(), ph.getRows());
				dg.setTotal(logicDao.count("select count(*) " + hql, params));
				for (Tlogic tlogic : tlogicList) {
					Logic logic2Page = new Logic();
					BeanUtils.copyProperties(tlogic, logic2Page, new String[] { "content" });
					copyAttributeTlogicToLogicWithoutNull(tlogic, logic2Page);
					logic2Page.setSummary(getLogicSummary(ClobUtil.getString(tlogic.getContent())));
					logicList.add(logic2Page);
				}
			} else {
				// 如果有输入，统一按照 搜索引擎 查询
				Analyzer chineseAnalyzer = new SmartChineseAnalyzer();
				QueryParser queryParser = new QueryParser("content", chineseAnalyzer);
				org.apache.lucene.search.Query luceneQuery = null;
				luceneQuery = queryParser.parse("content:" + logic.getTitle());
				Session session = sessionFactory.getCurrentSession();
				FullTextSession fullTextSession = Search.getFullTextSession(session);

				FullTextQuery ftquery = fullTextSession.createFullTextQuery(luceneQuery, Tlogic.class);
				// 设置总记录数 ftquery.getResultSize() 这个应该是获取匹配到关键字的数量，
				dg.setTotal(Long.valueOf(ftquery.getResultSize()));

				// 分页的实现
				ftquery.setFirstResult((ph.getPage() - 1) * ph.getRows());
				ftquery.setMaxResults(ph.getRows());

				// 返回 符合页码条数的记录
				tlogicList = ftquery.list();
				// 高亮设置
				SimpleHTMLFormatter formatter = new SimpleHTMLFormatter("<b><font color=\"red\">", "</font></b>");
				QueryScorer qs = new QueryScorer(luceneQuery);
				Highlighter highlighter = new Highlighter(formatter, qs);
				// 循环给每一个对象设置高亮
				for (Tlogic tlogicInHighlight : tlogicList) {
					tlogicErr = tlogicInHighlight;
					String findResult = "";
					// 获得 整个logic的内容
					String tlogicInHighlightContentString = ClobUtil.getString(tlogicInHighlight.getContent());
					// 设置返回长度
					Fragmenter fragmenter = new SimpleFragmenter(tlogicInHighlightContentString.length());
					highlighter.setTextFragmenter(fragmenter);
					// 开始根据搜索引起寻找关键字
					// 返回的是使用格式化关键字标记之后的结果
					findResult = highlighter.getBestFragment(chineseAnalyzer, logic.getTitle(),
							tlogicInHighlightContentString);
					// 由tlogic重新封装logic
					Logic logic2Page = new Logic();
					BeanUtils.copyProperties(tlogicInHighlight, logic2Page, new String[] { "content" });
					copyAttributeTlogicToLogicWithoutNull(tlogicInHighlight, logic2Page);
					logic2Page.setSummary(getLogicSummary(findResult));
					logicList.add(logic2Page);
				}

				// 对查询结果进行排序，Heat 大的排前
				Collections.sort(logicList, new Comparator<Object>() {
					@Override
					public int compare(Object o1, Object o2) {
						return ((Logic) o1).getHeat() < ((Logic) o2).getHeat() ? 1 : -1;
					}
				});
			}
		} catch (Exception e) {
			logger.error(" The error tlogic is : " + tlogicErr.getTitle(), e);
		}
		dg.setRows(logicList);
		return dg;
	}

	/**
	 * 功能：因为logic可能无对应版本与对应的产品，这里目的是做保护，防止空指针调用方法
	 * 
	 * @param tlogic
	 *            数据源的对象，必须非空
	 * @param logic
	 *            目的对象
	 */
	private void copyAttributeTlogicToLogicWithoutNull(Tlogic tlogic, Logic logic) {
		if (null == tlogic) {
			return;
		}
		if (null == tlogic.getTlogicfile()) {
			return;
		}
		Tversion tversion = tlogic.getTlogicfile().getTversion();
		// 当版本非空才有继续赋值的可能
		if (null != tversion) {
			logic.setVersionname(tversion.getName());
			// 当版本非空，产品才在非空的可能
			Tproduct tproduct = tversion.getTproduct();
			if (null != tproduct) {
				logic.setProductname(tproduct.getName());
			}
		}
	}

	/**
	 * 功能：由logic获取头信息作为整个逻辑的摘要 ；因为算法变化，所以逻辑头以上认为是注释，或者无注释
	 * 
	 * @param logicContent
	 *            整个logic的内容
	 * @return
	 */
	private String getLogicSummary(String logicContent) {
		String summary = "";
		// 逻辑名的模式
		String logicNameReg = "^(Logic|Join).*";
		Pattern logicNamePattern = Pattern.compile(logicNameReg);
		if (!StringUtil.isEmpty(logicContent)) {
			// 使用 <br /> 分割每一段的数据，最后补回<br />用来换行
			for (String data : logicContent.split("<br />")) {
				java.util.regex.Matcher matcher = logicNamePattern.matcher(data);
				if (matcher.matches()) {
					break;
				} else {
					summary = summary + data + "<br />";
				}
			}
		}
		return summary;
	}

	/**
	 * 排序函数
	 * 
	 * @param ph
	 *            分页对象
	 * @return
	 */
	private String orderHql(PageHelper ph) {
		String orderString = "";
		if (ph.getSort() != null && ph.getOrder() != null) {
			orderString = " order by t." + ph.getSort() + " " + ph.getOrder();
		}
		return orderString;
	}

	@Override
	public void add(Logic logic) {
		Tlogic tlogic = new Tlogic();
		BeanUtils.copyProperties(logic, tlogic, new String[] { "content", "summary" });
		tlogic.setContent(ClobUtil.getClob(logic.getContent()));
		tlogic.setCreatedatetime(new Date());
		logicDao.save(tlogic);
	}

	@Override
	public Logic get(String id) {
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("id", id);
		String hql = "from Tlogic t where t.id = :id";
		Tlogic tlogic = logicDao.get(hql, params);
		Logic logic = new Logic();
		BeanUtils.copyProperties(tlogic, logic, new String[] { "content", "summary" });
		logic.setContent(ClobUtil.getString(tlogic.getContent()));
		return logic;
	}

	@Override
	public void edit(Logic logic) {
		Tlogic tlogic = logicDao.get(Tlogic.class, logic.getId());
		if (null != tlogic) {
			BeanUtils.copyProperties(logic, tlogic, new String[] { "id", "content", "createdatetime", "summary" });
			tlogic.setModifydatetime(new Date());
			tlogic.setContent(ClobUtil.getClob(logic.getContent()));
		}
	}

	@Override
	public void delete(String id) {
		Tlogic tlogic = new Tlogic();
		try {
			tlogic = logicDao.get(Tlogic.class, id);
			logicDao.delete(tlogic);
		} catch (Exception e) {
			logger.error("The error logic is : " + tlogic.toString());
			logger.error(e.getMessage(), e);
		}

	}

	@Override
	public Boolean importLogic(File file, Logic logic) {
		Boolean result = false;
		// 解压rar文件
		try {
			String logicParentPath = file.getParent();
			if (RarUtil.untar(file, logicParentPath)) {
				// 读取logic文件，生成tlogic对象，存进数据库中
				String logicPath = logicParentPath + "/Logic";
				File logicDir = new File(logicPath);
				Map<String, Object> params = new HashMap<>();
				params.put("versionName", logic.getVersionname());
				// 获取版本信息
				Tversion tversion = versionDao.get("from Tversion t where t.name = :versionName", params);
				for (String fileName : logicDir.list()) {
					logger.info("logic file path : " + logicPath + "/" + fileName);
					if ("logic".equals(fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase())) {
						Tlogicfile tlfile = new Tlogicfile();
						tlfile.setId(UUID.randomUUID().toString());
						tlfile.setFileName(fileName);
						// 这里需要获取 文件 的摘要部分
						tlfile.setSummary(getLogicFileSummary(logicPath + "/" + fileName));
						// 这里获取 文件 里面全部的逻辑
						tlfile.setTlogicSet(splitLogic(logicPath + "/" + fileName, tlfile));
						tlfile.setTversion(tversion);
						tlogicfileDao.save(tlfile);
					} else {
						logger.info("该文件不是logic文件：" + fileName);
					}

				}
				result = true;
			}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		return result;
	}

	public String getLogicFileSummary(String logicFilePath) {
		String logicFileSummary = "";
		try {
			String data = null;
			String regEx = "^#!\\+==.*";
			Pattern pattern = Pattern.compile(regEx);
			BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(logicFilePath), "gbk"));
			while ((data = br.readLine()) != null) {
				java.util.regex.Matcher matcher = pattern.matcher(data);
				if (matcher.matches()) {
					logicFileSummary += data + "<br />";
					while ((data = br.readLine()) != null) {
						logicFileSummary += data + "<br />";
						matcher = pattern.matcher(data);
						if (matcher.matches()) {
							break;
						}
					}
					break;
				}
			}
			br.close();
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		return logicFileSummary;
	}

	@Override
	public Boolean exportLogic(String targetDirPath, Boolean deleteFlag) {
		Boolean result = false;
		// 将数据库中的tlogic对象生成logic文件放在logic目录中
		try {
			String logicDirPath = targetDirPath + "Logic/";
			File dirFile = new File(logicDirPath);
			if (!dirFile.exists()) {
				dirFile.mkdirs();
			}

			Map<String, Object> params = new HashMap<String, Object>();
			params.put("modifydatetime", new Date());

			List<Tlogic> tlogicList = logicDao.find("from Tlogic t where t.modifydatetime < :modifydatetime", params);
			// 判断是否有数据导出
			if (null != tlogicList && tlogicList.size() > 0) {
				FileWriter writer = new FileWriter(targetDirPath + "Logic/test.logic", true);
				for (Tlogic tlogic : tlogicList) {
					String content = ClobUtil.getString(tlogic.getContent());
					for (String s : content.split("<br />")) {
						// 把html的空格转换回文件的空格
						s = s.replaceAll("&nbsp;", " ");
						writer.write(s + "\n");
						// writer.flush();
					}
				}
				writer.close();

				// 将logic目录打包成zip压缩文件
				if (ZipUtil.zipCompressing(targetDirPath + "Logic.zip", targetDirPath + "Logic")) {
					result = true;
				}
				// 删除源文件
				if (deleteFlag) {
					FileUtils.deleteDirectory(new File(targetDirPath + "Logic"));
				}
			}

		} catch (IOException e) {
			logger.error(e.getMessage(), e);
		}
		return result;
	}

	private Set<Tlogic> splitLogic(String logicFilePath, Tlogicfile tlogicfile) throws IOException {
		Set<Tlogic> tlogics = new HashSet<Tlogic>();

		// 逻辑名的模式
		String logicNameReg = "^(Logic|Join)\\s+(\\w+)\\s\\{.*";
		Pattern logicNamePattern = Pattern.compile(logicNameReg);

		// 逻辑结尾的模式
		String endLogicReg = "\\s+}.*|}.*";
		Pattern endLogicPattern = Pattern.compile(endLogicReg);

		// 读取文件
		BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(logicFilePath), "gbk"));
		String data = null;
		String logicName = "";
		String logicBody = "";
		// 暂时存放结束符与下一个logic头中间部分
		String logicBodyTemp = "";

		Boolean endLogicFlag = false;
		while ((data = br.readLine()) != null) {
			// System.out.println(data);
			// 并且对每一行扫描，匹配出逻辑名
			java.util.regex.Matcher logicNameMatcher = logicNamePattern.matcher(data);

			if (logicNameMatcher.matches()) {
				if (endLogicFlag) {
					// System.out.println(logicBodyTemp);
					// System.out.println(logicBody);
					logicBody = logicBody.substring(logicBodyTemp.length());

					// 初始化logic对象
					Tlogic tlogic = new Tlogic();
					tlogic.setId(UUID.randomUUID().toString());
					tlogic.setTitle(logicName);
					// logicBodyTemp = logicBodyTemp.replaceAll(" ", "&nbsp;");
					tlogic.setContent(ClobUtil.getClob(logicBodyTemp.substring(0, logicBodyTemp.length() - 6)));

					// 保存logic对象
					tlogic.setTlogicfile(tlogicfile);
					logicDao.save(tlogic);
					tlogics.add(tlogic);

					// 获取下一个逻辑的名字
					logicName = logicNameMatcher.group(2);

				} else {
					logicName = logicNameMatcher.group(2);
				}
			}

			// 匹配是否一个逻辑的结束标记; 此处的匹配必须在data没有经过处理前，不然不能匹配}不在顶行的情况
			java.util.regex.Matcher endLogicmatcher = endLogicPattern.matcher(data);

			// 没遇到结尾之前的每一行，都直接存在变量里,把空格变为html的符号
			data = data.replaceAll(" ", "&nbsp;");
			logicBody += data + "<br />";

			// 如果匹配上
			if (endLogicmatcher.matches()) {
				// 暂时存放起来，继续寻找下一个logic头
				logicBodyTemp = logicBody;
				endLogicFlag = true;
				continue;
			}

		}

		// 当达到最后一行，当然也是处理最后一个logic
		// 初始化logic对象
		Tlogic tlogic = new Tlogic();
		tlogic.setId(UUID.randomUUID().toString());
		tlogic.setTitle(logicName);
		// logicBody = logicBody.replaceAll(" ", " &nbsp;");
		tlogic.setContent(ClobUtil.getClob(logicBody.substring(0, logicBody.length() - 6)));

		tlogic.setTlogicfile(tlogicfile);
		logicDao.save(tlogic);
		tlogics.add(tlogic);
		br.close();

		return tlogics;
	}

	// 废弃的逻辑分割算法，没考虑到无注释的情况
	@Deprecated
	private Set<Tlogic> splitLogic1(String logicFilePath, Tlogicfile tlogicfile) {
		Set<Tlogic> tlogics = new HashSet<Tlogic>();
		try {
			String logicString = "";
			String regEx = "^#!!==.*";
			Pattern pattern = Pattern.compile(regEx);
			String logicNameReg = "^(Logic|Join)\\s+(\\w+)\\s\\{.*";
			Pattern logicNamePattern = Pattern.compile(logicNameReg);
			String logicHead = "";
			BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(logicFilePath), "gbk"));
			String data = null;
			Boolean flag = false;
			while ((data = br.readLine()) != null) {
				java.util.regex.Matcher matcher = pattern.matcher(data);
				if (matcher.matches() | flag) {
					logicHead += data + "<br />";
					while ((data = br.readLine()) != null) {
						logicHead += data + "<br />";
						matcher = pattern.matcher(data);
						if (matcher.matches()) {
							logicString = logicHead;
							logicHead = "";
							flag = false;
							break;
						}
					}
				}

				java.util.regex.Matcher logicNameMatcher = logicNamePattern.matcher(data);
				if (logicNameMatcher.matches()) {
					String logicBody = "";
					String logicName = logicNameMatcher.group(2);
					Tlogic tlogic = new Tlogic();
					tlogic.setId(UUID.randomUUID().toString());
					tlogic.setTitle(logicName);
					tlogic.setCreatedatetime(new Date());
					tlogic.setHeat(0L);
					data = data.replaceAll(" ", "&nbsp;");
					logicBody += data + "<br />";
					while ((data = br.readLine()) != null) {
						matcher = pattern.matcher(data);
						data = data.replaceAll(" ", "&nbsp;");
						if (matcher.matches()) {
							logicHead += data + "<br />";
							flag = true;
							break;
						} else {
							logicBody += data + "<br />";
						}
					}
					logicString = logicString + logicBody;
					// 转换空格为html的 &nbsp;
					tlogic.setContent(ClobUtil.getClob(logicString));
					tlogic.setTlogicfile(tlogicfile);
					logicDao.save(tlogic);
					tlogics.add(tlogic);
				}

			}
			br.close();
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		return tlogics;
	}

}
